package com.qk.management.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.qk.common.PageResult;
import com.qk.common.constant.BusinessStatusConstants;
import com.qk.common.enums.*;
import com.qk.common.exception.BizException;
import com.qk.common.util.CurrentUserHolders;
import com.qk.dto.business.AddBusinessDTO;
import com.qk.dto.business.QueryPageBusinessDTO;
import com.qk.dto.business.QueryPoolBusinessDTO;
import com.qk.dto.business.TrackBusinessDTO;
import com.qk.entity.Business;
import com.qk.entity.BusinessTrackRecord;
import com.qk.entity.Customer;
import com.qk.management.mapper.BusinessMapper;
import com.qk.management.mapper.BusinessTrackRecordMapper;
import com.qk.management.mapper.CustomerMapper;
import com.qk.management.service.BusinessService;
import com.qk.vo.business.QueryBusinessVO;
import com.qk.vo.business.QueryPageBusinessVO;
import com.qk.vo.business.QueryPoolBusinessVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.statemachine.StateMachine;
import org.springframework.statemachine.config.StateMachineFactory;
import org.springframework.statemachine.service.StateMachineService;
import org.springframework.statemachine.support.DefaultStateMachineContext;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * @Author:gzc
 * @Date: 2025/10/06 21:54
 * @Description:
 */
@Service
@Slf4j
public class BusinessServiceImpl extends ServiceImpl<BusinessMapper, Business> implements BusinessService {

    @Autowired
    private CustomerMapper customerMapper;

    @Autowired
    private BusinessTrackRecordMapper businessTrackRecordMapper;

    @Autowired
    @Qualifier("businessStateMachineFactory")
    private StateMachineFactory<BusinessEnum, BusinessEvent> stateMachineFactory;

    @Autowired
    @Qualifier("businessStateMachineService")
    private StateMachineService<BusinessEnum, BusinessEvent> stateMachineService;

    @Override
    public PageResult<QueryPageBusinessVO> queryListByPage(QueryPageBusinessDTO queryPageBusinessDTO) {
        Page<QueryPageBusinessVO> page = this.baseMapper.queryListByPage(new Page<>(queryPageBusinessDTO.getPage(), queryPageBusinessDTO.getPageSize()), queryPageBusinessDTO);
        return PageResult.<QueryPageBusinessVO>builder().total(page.getTotal()).rows(page.getRecords()).build();
    }

    @Override
    public void addBusiness(AddBusinessDTO addBusinessDTO) {
        if (ObjectUtil.isEmpty(addBusinessDTO.getName())||ObjectUtil.isEmpty(addBusinessDTO.getPhone())) {
            BizException.throwBizException(BizEnum.PARAM_ERROR);
        }
        Business business = BeanUtil.copyProperties(addBusinessDTO, Business.class);
        business.setStatus(BusinessEnum.WAIT_ALLOCATION.getCode());
        business.setCreateTime(LocalDateTime.now());
        business.setUpdateTime(LocalDateTime.now());
        this.save(business);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void assignBusiness(Integer businessId, Integer userId) {
        if (ObjectUtil.isEmpty(businessId) || ObjectUtil.isEmpty(userId)) {
            BizException.throwBizException(BizEnum.PARAM_ERROR);
        }
        StateMachine<BusinessEnum, BusinessEvent> stateMachine = null;
        try {
            Business business = this.getById(businessId);
            stateMachine = getBusinessStateMachine(businessId);
            boolean transitionSuccess = stateMachine.sendEvent(BusinessEvent.ASSIGN);
            if (!transitionSuccess) {
                throw new Exception(BizEnum.BUSINESS_ALLOCATION_FAIL.getMsg());
            }
            BusinessEnum newState = stateMachine.getState().getId();
            business.setStatus(newState.getCode());
            business.setUserId(userId);
            business.setUpdateTime(LocalDateTime.now());
            this.updateById(business);
        }catch (DuplicateKeyException e){
            throw e;
        }catch (Exception e){
            log.error("分配商机异常：businessId={}", businessId, e);
            BizException.throwBizException(BizEnum.GENERATE_ERROR);
        }finally {
            releaseBusinessStateMachine(stateMachine);
        }
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void trackBusiness(TrackBusinessDTO trackBusinessDTO){
        boolean hasNull = BeanUtil.hasNullField(trackBusinessDTO, "gender", "age", "wechat", "qq", "subject", "courseId", "degree", "jobStatus", "channel", "remark", "userId", "clueId","status");
        if (hasNull) {
            BizException.throwBizException(BizEnum.PARAM_ERROR);
        }
        StateMachine<BusinessEnum, BusinessEvent> stateMachine = null;
        try {
            //更新商机信息
            if (Objects.equals(BusinessEnum.getByCode(trackBusinessDTO.getStatus()), BusinessEnum.WAIT_TRACK)) {
                stateMachine = getBusinessStateMachine(trackBusinessDTO.getId());
                boolean transitionSuccess = stateMachine.sendEvent(BusinessEvent.START_TRACK);
                if (!transitionSuccess) {
                    throw new Exception(BizEnum.BUSINESS_TRACK_FAIL.getMsg());
                }
                BusinessEnum newState = stateMachine.getState().getId();
                Business business = BeanUtil.copyProperties(trackBusinessDTO, Business.class);
                business.setStatus(newState.getCode());
                business.setUpdateTime(LocalDateTime.now());
                this.baseMapper.updateById(business);
            }
            //添加商机记录
            BusinessTrackRecord businessTrackRecord = BusinessTrackRecord.builder()
                    .businessId(trackBusinessDTO.getId())
                    .userId(CurrentUserHolders.getCurrentUserId())
                    .trackStatus(trackBusinessDTO.getTrackStatus())
                    .keyItems(trackBusinessDTO.getKeyItems().toString())
                    .nextTime(trackBusinessDTO.getNextTime())
                    .record(trackBusinessDTO.getRecord())
                    .createTime(LocalDateTime.now())
                    .build();
            businessTrackRecordMapper.insert(businessTrackRecord);
        }catch (BizException | DuplicateKeyException e){
            throw e;
        }catch (Exception e){
            log.error("跟进商机异常：businessId={}", trackBusinessDTO.getId(), e);
            BizException.throwBizException(BizEnum.GENERATE_ERROR);
        }finally {
            releaseBusinessStateMachine(stateMachine);
        }
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void backBusiness(Integer id) {
        if (ObjectUtil.isEmpty(id)) {
            BizException.throwBizException(BizEnum.PARAM_ERROR);
        }
       StateMachine<BusinessEnum, BusinessEvent> stateMachine = null;
        try {
            stateMachine = getBusinessStateMachine(id);
            boolean transitionSuccess = stateMachine.sendEvent(BusinessEvent.RECOVERY);
            if (!transitionSuccess) {
                throw new Exception(BizEnum.BUSINESS_RECOVERY_FAIL.getMsg());
            }
            BusinessEnum newState = stateMachine.getState().getId();
            Business business = this.getById(id);
            business.setStatus(newState.getCode());
            business.setUpdateTime(LocalDateTime.now());
            this.updateById(business);
        }catch (DuplicateKeyException e){
            throw e;
        }catch (Exception e){
            log.error("商机踢回公海异常：businessId={}", id, e);
            BizException.throwBizException(BizEnum.GENERATE_ERROR);
        }finally {
            releaseBusinessStateMachine(stateMachine);
        }
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void businessToCustomer(Integer id) {
        if (ObjectUtil.isEmpty(id)) {
            BizException.throwBizException(BizEnum.PARAM_ERROR);
        }
        StateMachine<BusinessEnum, BusinessEvent> stateMachine = null;
        try {
            //更新商机信息
            stateMachine = getBusinessStateMachine(id);
            boolean transitionSuccess = stateMachine.sendEvent(BusinessEvent.CONVERT_TO_CUS);
            if (!transitionSuccess) {
                throw new Exception(BizEnum.BUSINESS_CONVERT_CUSTOMER_FAIL.getMsg());
            }
            BusinessEnum newState = stateMachine.getState().getId();
            Business business = this.getById(id);
            business.setStatus(newState.getCode());
            business.setUpdateTime(LocalDateTime.now());
            this.baseMapper.updateById(business);
            //组装客户信息
            Customer customer = BeanUtil.copyProperties(business, Customer.class);
            customer.setId(null);
            customer.setBusinessId(business.getId());
            customer.setCreateTime(LocalDateTime.now());
            customer.setUpdateTime(LocalDateTime.now());
            customerMapper.insert(customer);
        }catch (DuplicateKeyException e){
            throw e;
        } catch (Exception e){
            log.error("商机转客户异常：businessId={}", id, e);
            BizException.throwBizException(BizEnum.GENERATE_ERROR);
        }finally {
            releaseBusinessStateMachine(stateMachine);
        }
    }

    @Override
    public QueryBusinessVO queryById(Integer id) {
        if (ObjectUtil.isEmpty(id)) {
            BizException.throwBizException(BizEnum.PARAM_ERROR);
        }
        return this.baseMapper.queryById(id);
    }



    @Override
    public PageResult<QueryPoolBusinessVO> queryBusinessPool(QueryPoolBusinessDTO queryPoolBusinessDTO) {
//        LambdaQueryWrapper<Business> queryWrapper = new LambdaQueryWrapper<>();
//        queryWrapper.eq(queryPoolBusinessDTO.getBusinessId() != null, Business::getId, queryPoolBusinessDTO.getBusinessId())
//                .apply(StringUtils.hasText(queryPoolBusinessDTO.getName()),
//                        "replace(business.name,' ','') like concat('%',replace({0},' ',''),'%')", queryPoolBusinessDTO.getName())
//                .apply(StringUtils.hasText(queryPoolBusinessDTO.getPhone()),
//                        "replace(business.phone,' ','') like concat(replace({0},' ',''),'%')", queryPoolBusinessDTO.getPhone())
//                .eq(queryPoolBusinessDTO.getSubject() != null, Business::getSubject, queryPoolBusinessDTO.getSubject())
//                .eq(Business::getStatus, BusinessStatusConstants.RECOVERY);
        Page<Business> page = this.baseMapper.selectPage(new Page<>(queryPoolBusinessDTO.getPage(), queryPoolBusinessDTO.getPageSize()), Wrappers.lambdaQuery(Business.class)
                .eq(queryPoolBusinessDTO.getBusinessId() != null, Business::getId, queryPoolBusinessDTO.getBusinessId())
                .apply(StringUtils.hasText(queryPoolBusinessDTO.getName()),
                        "replace(business.name,' ','') like concat('%',replace({0},' ',''),'%')", queryPoolBusinessDTO.getName())
                .apply(StringUtils.hasText(queryPoolBusinessDTO.getPhone()),
                        "replace(business.phone,' ','') like concat(replace({0},' ',''),'%')", queryPoolBusinessDTO.getPhone())
                .eq(queryPoolBusinessDTO.getSubject() != null, Business::getSubject, queryPoolBusinessDTO.getSubject())
                .eq(Business::getStatus, BusinessStatusConstants.RECOVERY));

        List<QueryPoolBusinessVO> voList = page.getRecords().stream()
                .map(business -> {
                    return BeanUtil.copyProperties(business, QueryPoolBusinessVO.class);
                })
                .collect(Collectors.toList());
        return PageResult.<QueryPoolBusinessVO>builder().total(page.getTotal()).rows(voList).build();
    }

    /**
     * 获取商机对应的状态机实例（每个商机一个实例，避免并发冲突）
     */
    private StateMachine<BusinessEnum, BusinessEvent> getBusinessStateMachine(Integer businessId) {
        // 1. 从数据库加载商机当前状态
        Business business = this.getById(businessId);
        BusinessEnum currentState = BusinessEnum.getByCode(business.getStatus());

        // 2. 自定义状态机上下文：指定初始状态为数据库状态，且ID为商机ID
        String machineId = businessId.toString();
        DefaultStateMachineContext<BusinessEnum, BusinessEvent> context =
                new DefaultStateMachineContext<>(
                        currentState,  // 初始状态设为数据库中的状态
                        null,          // 触发事件（初始化时无事件）
                        null,          // 触发器
                        null,          // 扩展变量
                        null,          // 消息头
                        machineId      // 状态机ID（与商机ID绑定）
                );

        // 3. 创建状态机实例，并应用自定义上下文（跳过默认初始状态）
        StateMachine<BusinessEnum, BusinessEvent> stateMachine = stateMachineFactory.getStateMachine(machineId);
        stateMachine.getStateMachineAccessor().doWithAllRegions(accessor -> {
            accessor.resetStateMachine(context); // 强制将状态机初始化为数据库状态
        });
        log.info("StateMachine id: " + stateMachine.getId());
        return stateMachine;
    }

    /**
     * 释放状态机实例
     */
    private void releaseBusinessStateMachine(StateMachine<BusinessEnum, BusinessEvent> stateMachine) {
        if (stateMachine == null){
            return;
        }
        stateMachineService.releaseStateMachine(stateMachine.getId());
    }
}
